﻿using System;

namespace Soft
{

    /// <summary>
    /// 哪一年的哪一周
    /// </summary>
    [Serializable]
    public struct Week : IComparable
    {
        private int mYear;

        private int mIndex;

        private DateTimeRange mRange;


        public Week(int year, int weekIndex)
        {
            this.mYear = year;
            this.mIndex = weekIndex;
            this.mRange = default(DateTimeRange);
            this.mRange = this.ResetRange(year, weekIndex);
        }

        public Week(DateTime date)
        {
            this.mRange = new DateTimeRange
            {
                Start = date.StartOf(TimeUnit.Week),
                End = date.EndOf(TimeUnit.Week)
            };
            this.mYear = this.mRange.Start.Year;
            this.mIndex = this.mRange.Start.WeekOfYear();
        }


        private DateTimeRange ResetRange(int year, int weekIndex)
        {
            if (year <= 0 || year > 9999)
            {
                throw new ArgumentOutOfRangeException(string.Format("年数据参数超出范围，正确的范围是：1 - 9999，当前值是:{0}", year));
            }
            Week week = Week.FirstWeekByYear(year);
            Week week2 = Week.LastWeekByYear(year);
            if (weekIndex >= week.Index && weekIndex <= week2.Index)
            {
                return week.AddWeek(weekIndex - week.Index).Range;
            }
            throw new ArgumentOutOfRangeException(string.Format("输入的周索引值超出范围，{0} 年的周范围是：{1} - {2}", year, week.Index, week2.Index));
        }

        public int Year
        {
            get
            {
                return this.mYear;
            }
            set
            {
                if (this.mYear != value)
                {
                    this.mYear = value;
                    this.mRange = this.ResetRange(this.mYear, this.mIndex);
                }
            }
        }

        public int Index
        {
            get
            {
                return this.mIndex;
            }
            set
            {
                if (this.mIndex != value)
                {
                    this.mIndex = value;
                    this.mRange = this.ResetRange(this.mYear, this.mIndex);
                }
            }
        }

        public DateTimeRange Range
        {
            get
            {
                return this.mRange;
            }
        }

        public double Value
        {
            get
            {
                return (double)this.Year + (double)this.Index / 100.0;
            }
        }

        public Week Add(int days)
        {
            return new Week(this.Range.Start.AddDays((double)days));
        }

        public Week AddWeek(int value)
        {
            return new Week(this.Range.Start.AddDays((double)(value * 7)));
        }

        public static Week Parse(object value)
        {
            string text = value.ToString();
            if (ToolsClass.RegexTest("^\\d+\\.\\d+$", text))
            {
                double num = value.ConvertTo(0.0, true);
                return new Week((int)num, (int)(num * 100.0) - (int)num * 100);
            }
            if (ToolsClass.RegexTest("^\\d{4}[0-5]\\d$", text))
            {
                return new Week(text.Substring(0, 4).ConvertTo(0, true), text.Substring(4, 2).ConvertTo(0, true));
            }
            throw new FormatException("转换成周数据的参数格式错误，正确的格式有包括浮点类型（yyyy.ww）和字符串类型（yyyyww），“yyyy”表示四位记年“ww”表示两位记周。");
        }

        public static Week FirstWeekByYear(int year)
        {
            if (year < 1)
            {
                year = 1;
            }
            else if (year > 9999)
            {
                year = 9999;
            }
            DateTime date = new DateTime(year, 1, 1);
            Week result = new Week(date);
            if (result.Index >= 2)
            {
                result = new Week(date.AddDays(7.0));
            }
            return result;
        }

        public static Week LastWeekByYear(int year)
        {
            if (year < 1)
            {
                year = 1;
            }
            else if (year > 9998)
            {
                year = 9998;
            }
            DateTime date = new DateTime(year, 12, 31);
            Week result = new Week(date);
            if (result.Index <= 2)
            {
                result = new Week(date.AddDays(-7.0));
            }
            return result;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return string.Format("{0:0.00}", this.Value);
        }

        public override bool Equals(object obj)
        {
            if (obj.GetType() == typeof(Week))
            {
                Week week = (Week)obj;
                return this.Year == week.Year && this.Index == week.Index;
            }
            return false;
        }

        public static Week Now
        {
            get
            {
                return new Week(DateTime.Now);
            }
        }

        public static int Compare(Week a, Week b)
        {
            return a.Value.CompareTo(b.Value);
        }

        public static bool operator ==(Week a, Week b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(Week a, Week b)
        {
            return !a.Equals(b);
        }

        public static bool operator >=(Week a, Week b)
        {
            return a.Value >= b.Value;
        }

        public static bool operator <=(Week a, Week b)
        {
            return a.Value <= b.Value;
        }

        public static bool operator >(Week a, Week b)
        {
            return a.Value > b.Value;
        }

        public static bool operator <(Week a, Week b)
        {
            return a.Value < b.Value;
        }

        public static int operator -(Week a, Week b)
        {
            return (int)(a.Range.Start - b.Range.Start).TotalDays / 7;
        }

        public static Week operator ++(Week a)
        {
            return a.AddWeek(1);
        }

        public int CompareTo(object obj)
        {
            if (obj != null && obj is Week)
            {
                return Week.Compare(this, (Week)obj);
            }
            return -1;
        }



        public static readonly Week Empty = new Week
        {
            mYear = 0,
            mIndex = 0
        };
    }
}